package com.umeox.watch.moto.dataservice.mqtt.service;

import android.app.AlarmManager;
import android.app.Dialog;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.text.TextUtils;
import android.util.Log;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.umeox.moto.common.log.Logger;
import com.umeox.moto.common.utils.AndroidUtils;
import com.umeox.moto.common.utils.DateUtils;
import com.umeox.moto.common.utils.NetUtils;
import com.umeox.moto.mqtt.MqttConfigure;
import com.umeox.moto.mqtt.ThreadTools;
import com.umeox.moto.mqtt.base.AError;
import com.umeox.moto.mqtt.base.ARequest;
import com.umeox.moto.mqtt.base.AResponse;
import com.umeox.moto.mqtt.base.PersistentConnectState;
import com.umeox.moto.mqtt.event.IConnectionStateListener;
import com.umeox.watch.moto.dataservice.base.BaseApp;
import com.umeox.watch.moto.dataservice.base.LauncherManager;
import com.umeox.watch.moto.dataservice.constants.Constants;
import com.umeox.watch.moto.dataservice.constants.InternalConstant;
import com.umeox.watch.moto.dataservice.mqtt.ConnectConfig;
import com.umeox.watch.moto.dataservice.mqtt.MessageCenter;
import com.umeox.watch.moto.dataservice.mqtt.Topic;
import com.umeox.watch.moto.dataservice.mqtt.impl.MqttClientWrapper;
import com.umeox.watch.moto.dataservice.mqtt.impl.MqttServiceDelegate;
import com.umeox.watch.moto.dataservice.mqtt.interfaces.IMqttClient;
import com.umeox.watch.moto.dataservice.mqtt.interfaces.IPublishListener;
import com.umeox.watch.moto.dataservice.mqtt.interfaces.IRequestListener;
import com.umeox.watch.moto.dataservice.mqtt.receiver.PingReceiver;
import com.umeox.watch.moto.dataservice.mqtt.request.PublishRequestFactory;
import com.umeox.watch.moto.dataservice.services.ApiService;
import com.umeox.watch.moto.dataservice.utils.LogUtils;
import com.umeox.watch.moto.dataservice.utils.Utils;

import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;

import java.util.Calendar;
import java.util.Locale;


public class MqttService extends Service implements IConnectionStateListener, MessageCenter.DeviceSecretRegisterListener {

    public static final String APP_ID = "com.wherecom.intent";
    public static final String MQTT_PUBLISH_MSG_INTENT = APP_ID + ".mqtt.SENDMSG";
    public static final String MQTT_PUBLISH_TOPIC = APP_ID + ".mqtt.SENDMSG_TOPIC";
    public static final String MQTT_PUBLISH_MSG = APP_ID + ".mqtt.SENDMSG_MSG";
    // constants used to tell the Activity UI the connection status
    public static final String MQTT_CONNECT_INTENT = APP_ID + ".mqtt.CONNECT";
    // constant used internally to schedule the next ping event
    public static final String MQTT_PING_ACTION = APP_ID + ".mqtt.PING";

    private static final String TAG = MqttService.class.getSimpleName();
    public static final String KEY_LAST_HANDLE_INTENT = "last_handle_intent";

    private static final String ACTION_VSIM_CONNECTED = "com.tgt.device.action.network.CONNECTED";

    private static boolean isServiceRunning = false;

    private Dialog mDialog = null;

    private IMqttClient mqttClient = null;
    private NetworkConnectionIntentReceiver netConnReceiver;
    private PingSender pingSender;
    private DialogDismissReceiver mDialogDismissReceiver;

    private volatile static MqttService instance;
    private int failConnectCount;

    public static MqttService getInstance() {
        if (instance == null) {
            synchronized (MqttService.class) {
                if (instance == null) {
                    instance = new MqttService();
                }
            }
        }
        return instance;
    }

    public boolean isRunning() {
        return isServiceRunning;
    }

    /**
     * 发送事件
     */
    public void sendEvent(String event, Object data, IPublishListener listener) {
        publishMsg(Topic.EVENT_PUBLISH_TOPIC_PREFIX, event, data, listener);
    }

    /**
     * 请求获取共享属性
     */
    public void requestAttributes(JSONArray attributes, IPublishListener listener) {
        publishMsg(Topic.GET_ATTRIBUTE_TOPIC_PREFIX, null, attributes, listener);
    }

    /**
     * 上传属性版本号
     */
    public void uploadHotVer(JSONObject attributes, IPublishListener listener) {
        publishMsg(Topic.DEVICE_ATTRIBUTE_POST_PREFIX, null, attributes, listener);
    }

    /**
     * 请求客户端Rpc
     */
    public void requestClientRpc(Object attributes, IPublishListener listener) {
        publishMsg(Topic.CLIENT_RPC_TOPIC_PREFIX, null, attributes, listener);
    }

    public void requestChatMsgClientRpc(String requestId, Object message, IPublishListener listener) {
        publishMsg(Topic.CLIENT_RPC_TOPIC_PREFIX, requestId, message, listener);
    }


    public void rpcResponse(String topic, String method, Object message, IPublishListener listener) {
        publishMsg(topic, method, message, listener);
    }


    public void ackPushMsg(String responseTopic, JSONObject ack) {
        publishMsg(responseTopic, null, ack, null);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        MessageCenter.getInstance().init(this);

        netConnReceiver = new NetworkConnectionIntentReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        intentFilter.addAction(ACTION_VSIM_CONNECTED);
        registerReceiver(netConnReceiver, intentFilter);

        pingSender = new PingSender();
        registerReceiver(pingSender, new IntentFilter(MQTT_PING_ACTION));

        mDialogDismissReceiver = new DialogDismissReceiver();
        registerReceiver(mDialogDismissReceiver, new IntentFilter("android.intent.action.POWEROFF_DIALOG"));


        isServiceRunning = true;
        Logger.i("MqttService onCreate...");
    }

    @Override
    public int onStartCommand(final Intent intent, int flags, final int startId) {
        Logger.i("onStartCommand: intent=" + intent + ", flags=" + flags + ", startId=" + startId + ", configure:" + LauncherManager.getInstance().getConfigure());
        doStart(intent);
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void doStart(final Intent intent) {
        if (System.currentTimeMillis() - BaseApp.getInstance().getMmkv().decodeLong(KEY_LAST_HANDLE_INTENT) > 5000) {
            BaseApp.getInstance().getMmkv().encode(KEY_LAST_HANDLE_INTENT, System.currentTimeMillis());
            handleStart(intent);
        }
    }

    private void handleStart(Intent intent) {
        Logger.i("handleStart");

        if (mqttClient == null) {
            Logger.i("handleStart: mqttClient == null");
            mqttClient = new MqttClientWrapper(this);
        }

        handleStartAction(intent);
    }

    private synchronized void handleStartAction(Intent intent) {
        String action = intent != null ? intent.getAction() : null;
        Logger.i("handleStartAction>>>" + action);
        if (action == null) {
            return;
        }
        if (action.equalsIgnoreCase(MQTT_CONNECT_INTENT)) {
            if (!isConnected() && NetUtils.isNetworkAvailable()) {
                LauncherManager.getInstance().setConfigure(new ConnectConfig(AndroidUtils.getDeviceId()));
                mqttClient.startConnect(LauncherManager.getInstance().getConfigure(), this);
            } else {
                Logger.i("client already connected or network is unavailable");
            }
        }
    }


    private void publishMsg(String topic, String event, Object message, IPublishListener listener) {
        if (mqttClient == null) {
            Logger.i("mqttClient is null");
        }

        boolean isOnline = NetUtils.isNetworkAvailable();
        boolean isConnected = isConnected();

        if (!isOnline || !isConnected) {
            Logger.i("publishMsg: isOnline()=" + isOnline + ", isConnected()=" + isConnected);
            AError error = new AError();
            error.setCode(AError.AKErrorNetworkError);
            if (!isOnline) {
                error.setMsg("设备当前无网络连接");
            } else {
                error.setMsg("MQTT长连接已断开");
            }
            if (listener != null) {
                listener.onFailure(PublishRequestFactory.createCommonPublishRequest(topic, message), message, error);
            }
            return;
        }

        mqttClient.asyncSendRequest(topic, event, message, new IRequestListener() {
            @Override
            public void onSuccess(ARequest request, AResponse response) {
                if (listener != null) {
                    listener.onSuccess(request, response);
                }
                // start the next keep alive period
                scheduleNextPing();
            }

            @Override
            public void onFailure(ARequest request, AError error) {
                if (listener != null) {
                    listener.onFailure(request, message, error);
                }
            }
        });


    }

    public boolean isConnected() {
        return (mqttClient != null && mqttClient.getConnectState() == PersistentConnectState.CONNECTED);
    }

    public boolean isConnecting() {
        return (mqttClient != null && mqttClient.getConnectState() == PersistentConnectState.CONNECTING);
    }

    @Override
    public void onConnectFail(String error) {
        Logger.e(TAG, "Connection fail>>>" + error);
        if (failConnectCount > 5) {
            return;
        }
        failConnectCount++;
        //5秒之后重新连接
        if (!isConnected() && !isConnecting()) {
            ThreadTools.submitTask(() -> {
                //5秒后重新构建连接参数尝试连接
                LauncherManager.getInstance().setConfigure(new ConnectConfig(AndroidUtils.getDeviceId()));
                MqttServiceDelegate.startService(MqttService.this);
            }, true, 5000);
        }
    }

    @Override
    public void onConnected() {
        Log.d(TAG, "onConnected: ");
        LauncherManager.getInstance().onNetworkAvailable();
        failConnectCount = 0;
        if (!TextUtils.isEmpty(BaseApp.getInstance().getMmkv().decodeString(InternalConstant.KEY_DEVICE_SECRET))) {
            Logger.d(TAG, "mqtt connected by deviceSecret：" + BaseApp.getInstance().getMmkv().decodeString(InternalConstant.KEY_DEVICE_SECRET));
            LogUtils.saveMqttLog("->mqtt connected by deviceSecret：" + BaseApp.getInstance().getMmkv().decodeString(InternalConstant.KEY_DEVICE_SECRET));
            //获取版本号进行同步
            ApiService.getHotVer();
            Intent intent = new Intent(Constants.ACTION_MTT_CONNECTED);
            intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES | Intent.FLAG_RECEIVER_FOREGROUND);
            BaseApp.getInstance().sendBroadcast(intent);
        } else {
            Logger.d(TAG, "mqtt connected not by deviceSecret");
            LogUtils.saveMqttLog("->mqtt connected not by deviceSecret");
        }
    }

    @Override
    public void onDisconnect() {
        if (!NetUtils.isNetworkAvailable()) {
            Logger.d(TAG, "Connection lost - no network connection");
            LogUtils.saveMqttLog("->Connection lost - no network connection");
            Utils.ping("www.baidu.com");
            Utils.ping("mimo-iot-mqtt-staging.maxcares.com");
        } else {
            Logger.d(TAG, "Connection lost - reconnecting...");
            LogUtils.saveMqttLog("->Connection lost - server disconnect");
            Utils.ping("www.baidu.com");
            Utils.ping("mimo-iot-mqtt-staging.maxcares.com");
        }
    }

    private void disconnectFromBroker() {
        Logger.i("disconnectFromBroker");

        try {
            if (netConnReceiver != null) {
                unregisterReceiver(netConnReceiver);
                netConnReceiver = null;
            }

            if (pingSender != null) {
                unregisterReceiver(pingSender);
                pingSender = null;
            }

            if (mDialogDismissReceiver != null) {
                unregisterReceiver(mDialogDismissReceiver);
                mDialogDismissReceiver = null;
            }
        } catch (Exception e) {
            Logger.e("unregister failed" + e.getMessage());
        }

        if (mqttClient != null) {
            mqttClient.destroy();
        }
        mqttClient = null;
    }

    public void scheduleNextPing() {

        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
                new Intent(PingReceiver.PING_RECEIVED_INTENT),
                PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager aMgr = (AlarmManager) getSystemService(ALARM_SERVICE);

        Calendar wakeUpTime = Calendar.getInstance();
        wakeUpTime.add(Calendar.SECOND, MqttConfigure.getKeepAliveInterval());


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            aMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, wakeUpTime.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            aMgr.setExact(AlarmManager.RTC_WAKEUP,
                    wakeUpTime.getTimeInMillis(),
                    pendingIntent);
        } else {
            aMgr.set(AlarmManager.RTC_WAKEUP,
                    wakeUpTime.getTimeInMillis(),
                    pendingIntent);
        }
        Logger.i(TAG, "Schedule Next Ping -> " + DateUtils.formatDateTime(wakeUpTime.getTimeInMillis()));
    }

    @Override
    public void onDestroy() {
        Logger.i("onDestroy");
        MessageCenter.getInstance().detach();
        // disconnect immediately
        disconnectFromBroker();
        isServiceRunning = false;
        super.onDestroy();
    }

    @Override
    public void onDeviceSecretRegistered() {
        // 重新构建连接参数连接MQTT
        mqttClient.disConnectManually(new IMqttActionListener() {
            @Override
            public void onSuccess(IMqttToken asyncActionToken) {
                Logger.i("mqtt disConnectManually success");
                BaseApp.getInstance().getMmkv().encode(KEY_LAST_HANDLE_INTENT, 0L);
                LauncherManager.getInstance().setConfigure(new ConnectConfig(AndroidUtils.getDeviceId()));
                MqttServiceDelegate.startService(MqttService.this);
            }

            @Override
            public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                Logger.i("mqtt disConnectManually fail, exception=" + exception.getMessage());
            }
        });

    }


    private class NetworkConnectionIntentReceiver extends BroadcastReceiver {


        @Override
        public void onReceive(Context ctx, Intent intent) {

            Log.d(TAG, "NetworkConnectionIntentReceiver onReceive: " + intent.getAction());

            Logger.i("onReceive: action= " + intent.getAction() + ",isOnline()=" + NetUtils.isNetworkAvailable() + ", isConnected()=" + isConnected());
            LogUtils.saveMqttLog("->onReceive: action= " + intent.getAction() + ",isOnline()=" + NetUtils.isNetworkAvailable() + ", isConnected()=" + isConnected());
            if (!isConnected()) {
                PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
                WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
                wl.acquire(1000L);
                MqttServiceDelegate.startService(MqttService.this);
                scheduleNextPing();
            }
        }
    }


    public class PingSender extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "onReceive: " + intent.getAction());
            if (NetUtils.isNetworkAvailable() && !isConnected()) {
                Logger.i(TAG, String.format(Locale.getDefault(), "network available(%1$b), broker connected(%2$b)", NetUtils.isNetworkAvailable(), isConnected()));
                Logger.i(TAG, "Waiting for mqtt reconnected");
                MqttServiceDelegate.startService(MqttService.this);
            } else if (!NetUtils.isNetworkAvailable()) {
                Logger.i(TAG, "Waiting for network to come online again");
            } else {
                Logger.i(TAG, "Broker connected, send PING message");
                long begin = System.currentTimeMillis();

                mqttClient.ping(new IRequestListener() {

                    @Override
                    public void onSuccess(ARequest request, AResponse aResponse) {
                        Logger.i(TAG, "ping success->time:" + (System.currentTimeMillis() - begin) + "ms");
                    }

                    @Override
                    public void onFailure(ARequest request, AError error) {
                        Logger.e(TAG, "ping failed - MQTT exception:" + error.getMsg());
                    }
                });
            }
            // start the next keep alive period
            scheduleNextPing();
        }
    }

    public class DialogDismissReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (mDialog != null) {
                mDialog.dismiss();
                unregisterReceiver(mDialogDismissReceiver);
            }
        }
    }

}
